package com.simpou.commons.utils.interceptor;

import java.lang.reflect.Method;


/**
 * Aplica o padrão "decorator" aos interceptadores. Use para encadear
 * interceptadores.
 *
 * @author Jonas Pereira
 * @since 2012-09-30
 * @version 2012-09-30
 */
public abstract class InterceptorDecorator extends AbstractInterceptor {
    /**
     * Intereceptador a ser chamado antes.
     */
    private final Interceptor interceptor;

    /**
     * @param interceptor Intereceptador a ser chamado antes.
     */
    public InterceptorDecorator(final Interceptor interceptor) {
        this.interceptor = interceptor;
    }

    /**
     * Ações a serem executadas antes do método interceptado e depois do método
     * correspondente do interceptador decorado.
     */
    public abstract void decorateBefore();

    @Override
    public void doBefore(final Method method, final Object[] args) {
        interceptor.doBefore(method, args);
        decorateBefore();
    }

    /**
     * Ações a serem executadas depois do método interceptado e depois do método
     * correspondente do interceptador decorado.
     */
    public abstract void decorateAfter();

    @Override
    public void doAfter(Object result) {
        interceptor.doAfter(result);
        decorateAfter();
    }

    /**
     * Ações a serem executadas se método interceptado lançar exceção e depois do método
     * correspondente do interceptador decorado.
     *
     * @param lastReturn Retorno do interceptador decorado para chamada do método correspondente.
     * @param throwable Exceção lançada pelo método intereceptado.
     * @return Resultado da interceptação.
     * @throws Throwable Exceção lançada, pode ser a mesma lançada anteriormente ou outra.
     */
    public abstract Object decorateOnError(final Object lastReturn,
        final Throwable throwable) throws Throwable;

    @Override
    public Object doOnError(final Throwable throwable)
        throws Throwable {
        Object lastReturn = interceptor.doOnError(throwable);

        return decorateOnError(lastReturn, throwable);
    }
}
